package db

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
)

type Table struct {
	Name           string
	ActualName     string
	ColName2Type   map[string]string
	ColNameInorder []string
	CreateStmt     string
	InsertStmt     string
}

// 初始化数据库表,读取 .sql 文件逐个表创建
func InitTables(sqlpath string) {
	AnalyzeTables(sqlpath)
	CreateTables()
}

func AnalyzeTables(sqlpath string) {
	file, err := os.Open(sqlpath)
	if err != nil {
		logger.Fatal("[AnalyzeTables]: %v", err)
	}
	defer file.Close()
	reader := bufio.NewReader(file)

	var alllines []string
	var tb *Table
	var insertCol []string
	var insertPlaceholder []string
	for {
		lineBytes, _, err := reader.ReadLine()
		if lineBytes != nil && err == nil {
			line := string(lineBytes)
			line = strings.TrimSpace(line)
			if len(line) > 0 {
				if strings.HasPrefix(line, "#") { //过滤注释行
					continue
				}

				if strings.HasPrefix(line, "create table") { //建表语句开始
					firstLine := strings.Split(line, " ")
					nameidx := len(firstLine) - 1
					tbname := firstLine[nameidx]
					tbname = strings.ToLower(tbname) //表名转换成小写
					_, ok := g_dbworker.Tables[tbname]
					if ok {
						panic("table name exists:" + tbname)
					}
					tb = &Table{
						Name:           tbname,
						ActualName:     fmt.Sprintf("%s.%s", g_dbworker.DBName, tbname), //再将表名由 dbname 换成 dbname.tbname
						ColName2Type:   make(map[string]string),
						ColNameInorder: make([]string, 0),
						CreateStmt:     "",
					}
					firstLine[nameidx] = tb.ActualName
					line = strings.Join(firstLine, " ")

					alllines = make([]string, 0)
					insertCol = make([]string, 0)
					insertPlaceholder = make([]string, 0)

					alllines = append(alllines, line)
					continue
				}
				alllines = append(alllines, line)

				if strings.HasPrefix(line, "(") || strings.HasPrefix(line, ")ENGINE") || strings.HasPrefix(line, "PARTITION") || strings.HasPrefix(line, "PRIMARY") {
					continue
				} else if strings.HasPrefix(line, ");") { //语句结束
					//建表语句
					tb.CreateStmt = strings.Join(alllines, "")
					//插入语句: INSERT INTO user (name, age) VALUES (?, ?)
					tb.InsertStmt = fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)",
						tb.ActualName, strings.Join(insertCol, ","), strings.Join(insertPlaceholder, ","))

					g_dbworker.Tables[tb.Name] = tb

					tb = nil
					alllines = nil
					insertCol = nil
					insertPlaceholder = nil
					continue
				} else { //每个字段类型解析, 只需要分析前两个
					varline := strings.Split(line, " ")
					colName := varline[0]
					colName = strings.TrimSpace(colName)
					colType := varline[1]
					//分析类型
					colType = strings.Split(colType, ",")[0] //省去右边的","
					colType = strings.Split(colType, "(")[0] //省去右边的"(xxx)" 类型长度
					colType = strings.TrimSpace(colType)
					//暂时仅支持这几种类型
					if !(colType == "int" || colType == "bigint" || colType == "double" || colType == "varchar" || colType == "DATETIME") {
						panic("colType error:" + colName + ":" + colType)
					}

					if colName != "inc_id" { //自增字段不用填
						tb.ColName2Type[colName] = colType
						tb.ColNameInorder = append(tb.ColNameInorder, colName)
						insertCol = append(insertCol, colName)

						if colType == "int" || colType == "bigint" {
							insertPlaceholder = append(insertPlaceholder, "%s")
						} else if colType == "varchar" || colType == "DATETIME" {
							insertPlaceholder = append(insertPlaceholder, "\"%s\"")
						} else if colType == "double" {
							insertPlaceholder = append(insertPlaceholder, "%s")
						}
					}
				}
			}
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			logger.Fatal("[AnalyzeTables]: %v", err)
		}
	}
	// for k, v := range g_dbworker.Tables {
	// 	fmt.Println("table:===", k)
	// 	for x, y := range v.ColName2Type {
	// 		fmt.Printf("%s %s\n", x, y)
	// 	}
	// 	//fmt.Println(v.CreateStmt)
	// 	fmt.Println(v.InsertStmt)
	// 	fmt.Println("===")
	// }
}

func CreateTables() {
	for tbname, v := range g_dbworker.Tables {
		stmt, err := g_dbworker.DB.Prepare(v.CreateStmt)
		if err != nil {
			logger.Fatal("[CreateTables]: tbname=%s,error=%v", tbname, err)
		}
		defer stmt.Close()

		_, err1 := stmt.Exec()
		if err1 != nil {
			logger.Fatal("[CreateTables]: tbname=%s,error=%v", tbname, err1)
		}
		logger.Info("[CreateTables]: ok.")
	}

	fmt.Println("initialize db tables ..")
}

func Insert(log string) {
	ret, str := AnalyzeLog(log)
	if !ret {
		logger.Info("[Insert]: fail, log=%s", log)
		fmt.Println(log)
		return
	}
	if !ExecStr(str) {
		fmt.Println("[Insert]: failed: ", str)
	}
}

// 解析日志格式,比如
// [2018-08-15 10:53:56][321][game1][Gold]: uid=702200321,addVal=9999,newTotal=9999,reason=gm指令增加金币
// 固定格式是 [日期][服务器id][进程名][事件名]
// 剩余的是各事件信息: key=value,...
// 注意,事件名 == 表名
func AnalyzeLog(log string) (bool, string) {
	//先把固定格式确定下来,不要用匹配,因为可能事件信息存在'[]'
	tmp := make(map[string]string)
	strs := make([]string, 4)
	for i := 0; i < 4; i++ {
		idx1 := strings.Index(log, "[")
		idx2 := strings.Index(log, "]")
		strs[i] = log[idx1+1 : idx2]
		log = log[idx2+1:]
	}
	//确定是否存在该表
	tbname := strings.ToLower(strs[3])
	tb, ok := g_dbworker.Tables[tbname]
	if !ok {
		fmt.Println("table name not exists:", tbname)
		return false, ""
	}
	//按顺序填入 tmp 映射
	tmp["logdate"] = strs[0]
	tmp["fromhost"] = strs[1]
	tmp["procname"] = strs[2]
	tmp["eventname"] = strs[3]

	//再把剩余的 k,v 解析出来
	//把"]:"冒号后面的字符串解析出来(日志格式是固定的)
	log = log[1:]
	//以","为分隔符, 遇到没有"="的子串则丢弃
	substr := strings.Split(log, ",")

	for _, v := range substr {
		kv := strings.Split(v, "=")
		if len(kv) == 2 {
			colName := strings.TrimSpace(kv[0])
			value := strings.TrimSpace(kv[1])
			tmp[colName] = value
		}
	}
	//检查一下是否全部参数都存在
	for colName, _ := range tb.ColName2Type {
		if _, ok := tmp[colName]; !ok {
			fmt.Println("colName not exists:", colName)
			return false, ""
		}
	}
	//按表的insert语句顺序插入到 newstrs
	size := len(tb.ColNameInorder)
	newstrs := make([]interface{}, size)
	for k, colName := range tb.ColNameInorder {
		value := tmp[colName]
		newstrs[k] = value
	}

	stmt := fmt.Sprintf(tb.InsertStmt, newstrs...)
	return true, stmt
}
